#include "bmp_output.h"

#include <fstream>
#include <cstring>

BmpOutput::BmpOutput(const std::string& path, uint width, uint height)
: Output(width, height), path(path), buffer(0)
{
}

BmpOutput::~BmpOutput()
{
	if(buffer)
	{
		delete[] buffer;
		buffer = 0;
	}
}

void BmpOutput::init()
{
	assert(buffer == 0);
	buffer = new BmpColor[width * height];
}

void BmpOutput::close()
{
	assert(buffer != 0);
	write();
}

// TODO: implement proper tone mapping
void BmpOutput::set_pixel(uint x, uint y, const Color& c)
{
	Color col = c;
	
	col.r = clamp(col.r, 0.0f, 1.0f);
	col.g = clamp(col.g, 0.0f, 1.0f);
	col.b = clamp(col.b, 0.0f, 1.0f);

	BmpColor bcol;
	bcol.r = byte(col.r * 255.0f);
	bcol.g = byte(col.g * 255.0f);
	bcol.b = byte(col.b * 255.0f);

	buffer[y * width + x] = bcol;
}

void BmpOutput::write()
{
	assert(sizeof(BmpColor) == 3);
	assert(sizeof(BmpFileHeader) == 14);
	assert(sizeof(BmpInfoHeader) == 40);

	BmpFileHeader file_h;
	BmpInfoHeader info_h;

	memset(&file_h, 0, sizeof(file_h));
	memset(&info_h, 0, sizeof(info_h));

	file_h.type = 19778;
	file_h.size = sizeof(file_h) + sizeof(info_h);
	file_h.offset = file_h.size;
	file_h.size += width * height * sizeof(BmpColor);

	info_h.size = sizeof(info_h);
	info_h.width = width;
	info_h.height = height;
	info_h.bpp = 24;

	uint real_row_size = width * sizeof(BmpColor);
	uint row_size = real_row_size;
	row_size += real_row_size % 4 ? (4 - real_row_size % 4) : 0;
	uint padding = row_size - real_row_size;

	std::ofstream output(path.c_str(), std::ios::binary);
	output.write((char*)&file_h, sizeof(file_h));
	output.write((char*)&info_h, sizeof(info_h));

	uint zero = 0;
	for(int y = height-1; y >= 0; --y)
	{
		output.write((char*)&buffer[y * width], real_row_size);
		if(padding)
			output.write((char*)&zero, padding);
	}
}
